using System.Collections.Generic;
using System;

namespace M13.Utility
{
    public interface IReference
    {
        void Clear();
    }
    
    public static class ReferencePool
    {
        private static readonly Dictionary<Type, ReferenceCollection> MReferenceCollections = new Dictionary<Type, ReferenceCollection>();
        public static int Count => MReferenceCollections.Count;//��ȡ���óص�����
        public static void ClearAll()
        {
            lock (MReferenceCollections)
            {
                foreach (var reference in MReferenceCollections.Values)
                {
                    reference.RemoveAll();
                }
                MReferenceCollections.Clear();
            }
        }

        public static t Acquire<t>() where t : class, IReference, new()
        {
            return GetReferenceCollection(typeof(t)).Acquire<t>();
        }

        public static void Release(IReference reference)
        {
            GetReferenceCollection(reference.GetType()).Release(reference);
        }

        public static void Add<t>(int count) where t : class, IReference, new()
        {
            GetReferenceCollection(typeof(t)).Add<t>(count);
        }

        public static void Remove<t>(int count) where t : class, IReference, new()
        {
            GetReferenceCollection(typeof(t)).Remove(count);
        }

        public static void RemoveAll<t>() where t : class, IReference, new()
        {
            GetReferenceCollection(typeof(t)).RemoveAll();
        }

        private static ReferenceCollection GetReferenceCollection(Type type)
        {
            if (type == null)
            {
                throw new Exception("Type ���� Ϊ��!!!");
            }
            ReferenceCollection referenceCollection = null;
            lock (MReferenceCollections)
            {
                if (!MReferenceCollections.TryGetValue(type, out referenceCollection))
                {
                    referenceCollection = new ReferenceCollection(type);
                    MReferenceCollections.Add(type, referenceCollection);
                }
            }
            return referenceCollection;
        }

        public static int GetCurrUsingRefCount<t>() where t : class, IReference, new()
        {
            return GetReferenceCollection(typeof(t)).CurrUsingRefCount;
        }
        public static int GetAcquireRefCount<t>() where t : class, IReference, new()
        {
            return GetReferenceCollection(typeof(t)).AcquireRefCount;
        }
        public static int GetReleaseRefCount<t>() where t : class, IReference, new()
        {
            return GetReferenceCollection(typeof(t)).ReleaseRefCount;
        }
        public static int GetAddRefCount<t>() where t : class, IReference, new()
        {
            return GetReferenceCollection(typeof(t)).AddRefCount;
        }
        public static int GetRemoveRefCount<t>() where t : class, IReference, new()
        {
            return GetReferenceCollection(typeof(t)).RemoveRefCount;
        }
    }
    public class ReferenceCollection
    {
        private readonly Queue<IReference> _mReferences = new Queue<IReference>();
        private Type _mReferenceType;
        private int _mCurrUsingRefCount;//��ǰ���õ�����
        private int _mAcquireRefCount;//�������õ�������
        private int _mReleaseRefCount;//�ͷ����õ�������
        private int _mAddRefCount;//�������õ�������
        private int _mRemoveRefCount;//�Ƴ����õ�������

        public int CurrUsingRefCount => _mCurrUsingRefCount;
        public int AcquireRefCount => _mAcquireRefCount;
        public int ReleaseRefCount => _mReleaseRefCount;
        public int AddRefCount => _mAddRefCount;
        public int RemoveRefCount => _mRemoveRefCount;


        public ReferenceCollection(Type refType)
        {
            _mReferenceType = refType;
            _mCurrUsingRefCount = 0;
            _mAcquireRefCount = 0;
            _mReleaseRefCount = 0;
            _mAddRefCount = 0;
            _mRemoveRefCount = 0;
        }

        public t Acquire<t>() where t : class, IReference, new()
        {
            if (typeof(t) != _mReferenceType)
            {
                throw new Exception("���Ͳ���ͬ�޷�����!!!");
            }
            _mCurrUsingRefCount++;
            _mAcquireRefCount++;
            lock (_mReferences)
            {
                if (_mReferences.Count > 0)
                {
                    return (t)_mReferences.Dequeue();
                }
            }
            _mAddRefCount++;
            return new t();
        }

        public void Release(IReference reference)
        {
            reference.Clear();
            lock (_mReferences)
            {
                if (_mReferences.Contains(reference))
                {
                    throw new Exception("�����Ѿ����ͷţ����������ͷ�!!!");
                }

                _mReferences.Enqueue(reference);
            }

            _mCurrUsingRefCount--;
            _mReleaseRefCount++;
        }

        public void Add<t>(int count) where t : class, IReference, new()
        {
            if (typeof(t) != _mReferenceType)
            {
                throw new Exception("���Ͳ���ͬ�޷�����!!!");
            }
            lock (_mReferences)
            {
                _mAddRefCount += count;
                while (count-- > 0)
                {
                    _mReferences.Enqueue(new t());
                }
            }
        }

        public void Remove(int count)
        {
            lock (_mReferences)
            {
                if (count > _mReferences.Count)
                {
                    count = _mReferences.Count;
                }
                _mRemoveRefCount += count;
                while (count-- > 0)
                {
                    _mReferences.Dequeue();
                }
            }
        }

        public void RemoveAll()
        {
            lock (_mReferences)
            {
                _mRemoveRefCount += _mReferences.Count;
                _mReferences.Clear();
            }
        }
    }

}
